using System;
using System.Data;
using System.IO;
using System.Reflection;
using SecuBat.AttackPlugin;
using SecuBat.Crawling.Forms;
using Css.Diagnostics;
using Microsoft.Practices.EnterpriseLibrary.Data;
using System.Data.Common;
using SecuBat.Logging;

namespace SecuBat.Modularization.Plugins
{
	/// <summary>
	/// Summary description for Plugin.
	/// </summary>
	public class Plugin
	{
		private int _id;
		private String _filename;
		private String _name;
		private String _description;
		private String _version;
		private String _attackTypeName;
		private String _analysisTypeName;
		private String _commonDataTypeName;
		private bool _isActive;
		private PluginState _state;
		private Type _attackType;
		private Type _analysisType;
		private Type _commonDataType;

		public int Id
		{
			get { return _id; }
		}

		public string Filename
		{
			get { return _filename; }
			set { _filename = value; }
		}

		public string Name
		{
			get { return _name; }
			set { _name = value; }
		}

		public string Description
		{
			get { return _description; }
			set { _description = value; }
		}

		public String Version
		{
			get { return _version; }
			set { _version = value; }
		}

		public bool IsActive
		{
			get { return _isActive; }
			set { _isActive = value; }
		}

		public bool IsRegistered
		{
			get { return Id > -1; }
		}

		public PluginState State
		{
			get { return _state; }
			set { _state = value; }
		}

		public string AttackTypeName
		{
			get { return _attackTypeName; }
			set { _attackTypeName = value; }
		}

		public string AnalysisTypeName
		{
			get { return _analysisTypeName; }
			set { _analysisTypeName = value; }
		}

		public string CommonDataTypeName
		{
			get { return _commonDataTypeName; }
			set { _commonDataTypeName = value; }
		}

		public Plugin(String filename)
		{
			Filename = filename;
			Name = "";
			Description = "";
			Version = "";
			AttackTypeName = "";
			AnalysisTypeName = "";
			CommonDataTypeName = "";
			IsActive = true;
			State = PluginState.None;
			_attackType = null;
			_analysisType = null;
			_commonDataType = null;
			this.LoadAssembly();
		}

		public Plugin(String filename, String name, String description, String version, String attackType, String analysisType, String commonDataType)
		{
			Filename = filename;
			Name = name;
			Description = description;
			Version = version;
			AttackTypeName = attackType;
			AnalysisTypeName = analysisType;
			CommonDataTypeName = commonDataType;
			IsActive = true;
			State = PluginState.None;
			_attackType = null;
			_analysisType = null;
			_commonDataType = null;
		}

		public Plugin(IDataReader reader)
		{
			if (reader != null)
			{
				try
				{
					_id = reader.GetInt32(0);
					Filename = reader.GetString(1);
					Name = reader.GetString(2);
					Description = reader.GetString(3);
					Version = reader.GetString(4);
					AttackTypeName = reader.GetString(5);
					AnalysisTypeName = reader.GetString(6);
					if (reader.IsDBNull(7))
						CommonDataTypeName = null;
					else
						CommonDataTypeName = reader.GetString(7);
					IsActive = Convert.ToBoolean(reader.GetInt32(8));
				}
				catch (Exception ex)
				{
					LogManager.Write(TraceLevel.Error, ex, "Plugin loading");
				}
			}
			State = PluginState.None;
			_attackType = null;
			_analysisType = null;
			_commonDataType = null;
		}

		public void FillCommand(Database db, DbCommand command)
		{
			if (command != null)
			{
				db.AddInParameter(command, "@i_FileName", DbType.String, Filename);
				db.AddInParameter(command, "@i_Name", DbType.String, Name);
				db.AddInParameter(command, "@i_Description", DbType.String, Description);
				db.AddInParameter(command, "@i_Version", DbType.String, Version);
				db.AddInParameter(command, "@i_AttackType", DbType.String, AttackTypeName);
				db.AddInParameter(command, "@i_AnalysisType", DbType.String, AnalysisTypeName);
				if (CommonDataTypeName != null)
					db.AddInParameter(command, "@i_CommonDataType", DbType.String, CommonDataTypeName);
				else
					db.AddInParameter(command, "@i_CommonDataType", DbType.String, DBNull.Value);
				db.AddInParameter(command, "@i_IsActive", DbType.Int32, IsActive);
			}
		}

		public bool Register()
		{
			try
			{
				Database db = DatabaseFactory.CreateDatabase();
				DbCommand command = db.GetStoredProcCommand("newEPlugin");
				FillCommand(db, command);
				db.AddOutParameter(command, "o_id", DbType.Int32, 4);
				db.ExecuteNonQuery(command);
				_id = (int) db.GetParameterValue(command, "o_id");
				if (_id > 0)
					return true;
			}
			catch (Exception ex)
			{
				LogManager.Write(TraceLevel.Error, ex, "Plugin registration");
			}
			return false;
		}

		public bool Update()
		{
			try
			{
				Database db = DatabaseFactory.CreateDatabase();
				DbCommand command = db.GetStoredProcCommand("setEPlugin");
				db.AddInParameter(command, "i_ID", DbType.Int32, this.Id);
				FillCommand(db, command);
                if (db.ExecuteNonQuery(command) > 0)
					return true;
			}
			catch (Exception ex)
			{
				LogManager.Write(TraceLevel.Error, ex, "Plugin update");
			}
			return false;
		}

		public void LoadAssembly()
		{
			AttackTypeName = "";
			AnalysisTypeName = "";
			CommonDataTypeName = "";
			if (File.Exists(Filename))
			{
				try
				{
					Assembly assembly = null;
					assembly = Assembly.LoadFile(Filename);
					
					Name = ((AssemblyTitleAttribute) AssemblyTitleAttribute.GetCustomAttribute(assembly, typeof(AssemblyTitleAttribute))).Title;
					Description = ((AssemblyDescriptionAttribute) AssemblyDescriptionAttribute.GetCustomAttribute(assembly, typeof(AssemblyDescriptionAttribute))).Description;
					Version = assembly.GetName().Version.ToString();
 
					if (null != assembly)
					{
						foreach (Type type in assembly.GetTypes())
						{
							if (type.IsAbstract) continue;
							if (type.IsDefined(typeof(SecuBat.AttackPlugin.AttackPluginAttribute), true))
							{
								AttackTypeName = type.FullName;
								_attackType = type;
							}
							else if (type.IsDefined(typeof(SecuBat.AttackPlugin.AnalysisPluginAttribute), true))
							{
								AnalysisTypeName = type.FullName;
								_analysisType = type;
							}
							else if (type.IsDefined(typeof(SecuBat.AttackPlugin.CommonDataPluginAttribute), true))
							{
								CommonDataTypeName = type.FullName;
								_commonDataType = type;
							}
						}
						
						if (AttackTypeName.Length < 1 && AnalysisTypeName.Length < 1)
						{
							State = PluginState.NoAttackNoAnalysis;
						}
						else if (AttackTypeName.Length < 1)
						{
							State = PluginState.NoAttack;
						}
						else if (AnalysisTypeName.Length < 1)
						{
							State = PluginState.NoAnalysis;
						}
					}
				}
				catch (System.InvalidCastException)
				{
					LogManager.Write(TraceLevel.Error, "The attack and analysis objects do not implement the correct interfaces.", "Plugin Loading");
					State = PluginState.WrongInterfaces;
				}
				catch (System.BadImageFormatException)
				{
					LogManager.Write(TraceLevel.Error, "Invalid plugin file: " + Filename, "Plugin Loading");
					State = PluginState.Incompatible;
				}
				catch ( System.Exception ex )
				{
					LogManager.Write(TraceLevel.Error, ex, "Plugin Loading");
					State = PluginState.GeneralError;
				}
		
			}
			else
				State = PluginState.NotFound;

			if (AttackTypeName.Length > 0 && AnalysisTypeName.Length > 0)
				State = PluginState.Loaded;
		}

		public IAttack GetAttackInstance(int attackerRunId, int attackFormId, int pluginId, FormParameter submitButton, IAnalysis mainAnalysis, ICommonData commonData)
		{
			if (State == PluginState.Loaded && _attackType != null)
			{
				IAttack attack = (IAttack) Activator.CreateInstance(_attackType, new object[] {attackerRunId, attackFormId, pluginId, submitButton, mainAnalysis, commonData});
				return attack;
			}
			return null;
		}

		public IAnalysis GetAnalysisInstance(ICommonData data)
		{
			if (State == PluginState.Loaded && _analysisType != null)
			{
				IAnalysis analysis = (IAnalysis) Activator.CreateInstance(_analysisType, new object[] {data});
				return analysis;
			}
			return null;
		}

		public ICommonData GetCommonDataInstance()
		{
			if (State == PluginState.Loaded && _commonDataType != null)
			{
				ICommonData commonData = (ICommonData) Activator.CreateInstance(_commonDataType);
				return commonData;
			}
			return null;
		}

	}
}
